Plumb network vif credit-based rate limiting thorugh xenbus
authorkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Fri, 31 Mar 2006 14:34:52 +0000 (15:34 +0100)
committerkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Fri, 31 Mar 2006 14:34:52 +0000 (15:34 +0100)
and xend into xm guest config files.

A new vif parameter 'rate' is supported, with an optional time window
paremeter for specifying granularity of credit replenishment. The default
window is 50ms. For example:

 'rate=10Mb/s'  'rate=250KB/s'  'rate=1MB/s@20ms'

From: Chris Clark <christopher.w.clark@gmail.com>

Signed-off-by: Keir Fraser <keir@xensource.com>
linux-2.6-xen-sparse/drivers/xen/netback/common.h
linux-2.6-xen-sparse/drivers/xen/netback/interface.c
linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c
tools/python/xen/xend/server/netif.py
tools/python/xen/xm/create.py

index ab52a03f07d70adb492a0f05e495b39e74b04348..e328b63e54bfb6a951dfb7123e1dcfd41f32660d 100644 (file)
@@ -97,7 +97,6 @@ typedef struct netif_st {
 #define NET_TX_RING_SIZE __RING_SIZE((netif_tx_sring_t *)0, PAGE_SIZE)
 #define NET_RX_RING_SIZE __RING_SIZE((netif_rx_sring_t *)0, PAGE_SIZE)
 
-void netif_creditlimit(netif_t *netif);
 void netif_disconnect(netif_t *netif);
 
 netif_t *alloc_netif(domid_t domid, unsigned int handle, u8 be_mac[ETH_ALEN]);
index 79944d6440de68d922a95dd65c38c3189cee8b06..be96a6d3ac72b1b20bf6194760233505018399bd 100644 (file)
@@ -293,25 +293,6 @@ void free_netif(netif_t *netif)
        schedule_work(&netif->free_work);
 }
 
-void netif_creditlimit(netif_t *netif)
-{
-#if 0
-       /* Set the credit limit (reset remaining credit to new limit). */
-       netif->credit_bytes     = creditlimit->credit_bytes;
-       netif->remaining_credit = creditlimit->credit_bytes;
-       netif->credit_usec      = creditlimit->period_usec;
-
-       if (netif->status == CONNECTED) {
-               /*
-                * Schedule work so that any packets waiting under previous
-                * credit limit are dealt with (acts as a replenishment point).
-                */
-               netif->credit_timeout.expires = jiffies;
-               netif_schedule_work(netif);
-       }
-#endif
-}
-
 void netif_disconnect(netif_t *netif)
 {
        switch (netif->status) {
index ea9a02e28e7acd8d4d53d8d96c8115ef8b5125f1..d270f099c4767f104ebbfd56a56a81087d84ed81 100644 (file)
@@ -233,9 +233,44 @@ static void frontend_changed(struct xenbus_device *dev,
 
 static void maybe_connect(struct backend_info *be)
 {
-       if (be->netif != NULL && be->frontend_state == XenbusStateConnected) {
+       if (be->netif && (be->frontend_state == XenbusStateConnected))
                connect(be);
-       }
+}
+
+static void xen_net_read_rate(struct xenbus_device *dev,
+                             unsigned long *bytes, unsigned long *usec)
+{
+       char *s, *e;
+       unsigned long b, u;
+       char *ratestr;
+
+       /* Default to unlimited bandwidth. */
+       *bytes = ~0UL;
+       *usec = 0;
+
+       ratestr = xenbus_read(XBT_NULL, dev->nodename, "rate", NULL);
+       if (IS_ERR(ratestr))
+               return;
+
+       s = ratestr;
+       b = simple_strtoul(s, &e, 10);
+       if ((s == e) || (*e != ','))
+               goto fail;
+
+       s = e + 1;
+       u = simple_strtoul(s, &e, 10);
+       if ((s == e) || (*e != '\0'))
+               goto fail;
+
+       *bytes = b;
+       *usec = u;
+
+       kfree(ratestr);
+       return;
+
+ fail:
+       WPRINTK("Failed to parse network rate limit. Traffic unlimited.\n");
+       kfree(ratestr);
 }
 
 
@@ -254,6 +289,10 @@ static void connect(struct backend_info *be)
                return;
        }
 
+       xen_net_read_rate(dev, &be->netif->credit_bytes,
+                         &be->netif->credit_usec);
+       be->netif->remaining_credit = be->netif->credit_bytes;
+
        xenbus_switch_state(dev, XenbusStateConnected);
 }
 
index 028bbf3989072034a3683b67cf257d4f64cfa2d8..94016e3628453ed99866a1c2141465a09d70c6f6 100644 (file)
@@ -22,6 +22,7 @@
 
 import os
 import random
+import re
 
 from xen.xend import sxp
 from xen.xend import XendRoot
@@ -50,6 +51,86 @@ def randomMAC():
             random.randint(0x00, 0xff) ]
     return ':'.join(map(lambda x: "%02x" % x, mac))
 
+rate_re = re.compile("^([0-9]+)([GMK]?)([Bb])/s(@([0-9]+)([mu]?)s)?$")
+
+def parseRate(ratestr):
+    """if parsing fails this will return default of unlimited rate"""
+    bytes_per_interval = 0xffffffffL # 0xffffffff # big default
+    interval_usecs     = 0L          # disabled
+
+    m = rate_re.match(ratestr)
+    if m:
+        bytes_per_sec = long(m.group(1))
+
+        if m.group(2) == 'G':
+            bytes_per_sec *= 1000 * 1000 * 1000
+        elif m.group(2) == 'M':
+            bytes_per_sec *= 1000 * 1000
+        elif m.group(2) == 'K':
+            bytes_per_sec *= 1000
+
+        if m.group(3) == 'b':
+            bytes_per_sec /= 8
+
+        if m.group(5) is None:
+            interval_usecs = 50000L      # 50ms default
+        else:
+            interval_usecs = long(m.group(5))
+            if m.group(6) == '':
+                interval_usecs *= 1000 * 1000
+            elif m.group(6) == 'm':
+                interval_usecs *= 1000
+
+        bytes_per_interval = (bytes_per_sec * interval_usecs) / 1000000L
+
+        # overflow / underflow checking: default to unlimited rate
+        if bytes_per_interval == 0 or bytes_per_interval > 0xffffffffL or \
+           interval_usecs == 0 or interval_usecs > 0xffffffffL:
+            bytes_per_interval = 0xffffffffL
+            interval_usecs     = 0L
+
+    return "%lu,%lu" % (bytes_per_interval, interval_usecs)
+
+
+write_rate_G_re = re.compile('^([0-9]+)000000000(B/s@[0-9]+us)$')
+write_rate_M_re = re.compile('^([0-9]+)000000(B/s@[0-9]+us)$')
+write_rate_K_re = re.compile('^([0-9]+)000(B/s@[0-9]+us)$')
+write_rate_s_re = re.compile('^([0-9]+[GMK]?B/s@[0-9]+)000000us$')
+write_rate_m_re = re.compile('^([0-9]+[GMK]?B/s@[0-9]+)000us$')
+
+def formatRate(rate):
+    (bytes_per_interval, interval_usecs) = map(long, rate.split(','))
+
+    if interval_usecs != 0:
+        bytes_per_second = (bytes_per_interval * 1000 * 1000) / interval_usecs
+    else:
+        bytes_per_second = 0xffffffffL
+
+    ratestr = "%uB/s@%uus" % (bytes_per_second, interval_usecs)
+
+    # look for '000's
+    m = write_rate_G_re.match(ratestr)
+    if m:
+        ratestr = m.group(1) + "G" + m.group(2)
+    else:
+        m = write_rate_M_re.match(ratestr)
+        if m:
+            ratestr = m.group(1) + "M" + m.group(2)
+        else:
+            m = write_rate_K_re.match(ratestr)
+            if m:
+                ratestr = m.group(1) + "K" + m.group(2)
+
+    m = write_rate_s_re.match(ratestr)
+    if m:
+        ratestr = m.group(1) + "s"
+    else:
+        m = write_rate_m_re.match(ratestr)
+        if m:
+            ratestr = m.group(1) + "ms"
+
+    return ratestr
+
 
 class NetifController(DevController):
     """Network interface controller. Handles all network devices for a domain.
@@ -75,6 +156,7 @@ class NetifController(DevController):
         bridge  = sxp.child_value(config, 'bridge')
         mac     = sxp.child_value(config, 'mac')
         vifname = sxp.child_value(config, 'vifname')
+        rate    = sxp.child_value(config, 'rate')
         ipaddr  = _get_config_ipaddr(config)
 
         devid = self.allocateDeviceID()
@@ -98,6 +180,8 @@ class NetifController(DevController):
             back['bridge'] = bridge
         if vifname:
             back['vifname'] = vifname
+        if rate:
+            back['rate'] = parseRate(rate)
 
         return (devid, back, front)
 
@@ -107,8 +191,8 @@ class NetifController(DevController):
 
         result = DevController.configuration(self, devid)
 
-        (script, ip, bridge, mac, typ, vifname) = self.readBackend(
-            devid, 'script', 'ip', 'bridge', 'mac', 'type', 'vifname')
+        (script, ip, bridge, mac, typ, vifname, rate) = self.readBackend(
+            devid, 'script', 'ip', 'bridge', 'mac', 'type', 'vifname', 'rate')
 
         if script:
             result.append(['script',
@@ -125,5 +209,7 @@ class NetifController(DevController):
             result.append(['type', typ])
         if vifname:
             result.append(['vifname', vifname])
+        if rate:
+            result.append(['rate', formatRate(rate)])
 
         return result
index 2933af5e4fe0b7188609753591330819aeae6ec1..6da9ed37113ae9f059009090491227454d27e41b 100644 (file)
@@ -552,7 +552,7 @@ def configure_vifs(config_devs, vals):
 
         def f(k):
             if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
-                         'vifname']:
+                         'vifname', 'rate']:
                 err('Invalid vif option: ' + k)
 
             config_vif.append([k, d[k]])